const cryptoJs = require('crypto-js');
var Buffer = require('buffer').Buffer;
const base32 = require('./base32');
const hex = require('crypto-js/enc-hex');

export default class {
  constructor(length = 6, ts = 30, algo = 'SHA1') {
    this.length = length;
    this.ts = ts;
    this.algo = algo;
  }

  getCode(secret, time = null) {
    const key = base32.decode(secret);
    time = time || Math.floor((new Date).getTime() / 1000 / this.ts)
    const buffer = Buffer.alloc(8);
    buffer.writeInt32BE(time, 4);
    const algoFunc = this.getAlgoFunc();
    const hexString = algoFunc(cryptoJs.enc.Hex.parse(buffer.toString('hex')), cryptoJs.enc.Hex.parse(key.toString('hex'))).toString(hex);
    const token = Buffer.alloc(hexString.length, hexString, 'hex');
    const offset = token[token.length - 1] & 0xF;
    const randomBytes = token.slice(offset, offset + 4);
    const modulo = Math.pow(10, this.length);
    const value = randomBytes.readInt32BE() & 0x7FFFFFFF;
    let code = value % modulo + '';
    while (code.length < this.length) code = '0' + code;
    return code;
  }

  getAlgoFunc(){
    switch(this.algo.toUpperCase()) {
      case 'SHA1':
        return cryptoJs.HmacSHA1;
      case 'SHA256':
        return cryptoJs.HmacSHA256;
      case 'SHA512':
        return cryptoJs.HmacSHA512;
      default:
        return cryptoJs.HmacSHA1;
    }
  }

  refreshTime() {
    return this.ts - Math.floor((new Date).getTime() / 1000 % this.ts);
  }
}
